/**
 *
 * jcrt and its source-code is licensed under the LGPL.
 * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
 * When copying, adapting, or redistributing this document in keeping with the guidelines above,
 * you are required to provide proper attribution to obinary.
 * If you reproduce or distribute the document without making any substantive modifications to its content,
 * please use the following attribution line:
 *
 * Copyright 2007 Atjava.com Ltd. (http://www.atjava.com) All rights reserved.
 *
 */
package com.atjava.jcrt.core;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.QueryManager;

import org.apache.log4j.Logger;

import com.atjava.jcrt.BaseInfo;
import com.atjava.jcrt.JcrtSession;
import com.atjava.jcrt.core.exception.CoreException;
import com.atjava.jcrt.core.query.ConditionData;
import com.atjava.jcrt.core.query.Query;
import com.atjava.jcrt.core.query.QueryImpl;
import com.atjava.jcrt.core.transaction.XmlJcrtInfo;
import com.atjava.jcrt.core.version.Version;
import com.atjava.jcrt.core.version.VersionImpl;
import com.atjava.jcrt.repository.Provider;
import com.atjava.jcrt.utility.DependHelper;
import com.atjava.jcrt.utility.UtilHelper;
/**
 * 
 * @author biggie
 * @version $Revision: 1.11 $ $Date: 2008/07/23 07:18:38 $
 */
public class JcrtSessionImpl implements JcrtSession {
	
	private static final long serialVersionUID = 1L;
    /**
     * Log4j
     */
    private Logger log = Logger.getLogger(this.getClass().getName());
    
	private Provider provider = null;
	private Session session = null;
	private XmlJcrtInfo jcrtInfo = null;
		
	public JcrtSessionImpl(Provider provider,XmlJcrtInfo jcrtInfo) throws CoreException{
		this.provider = provider;
		this.jcrtInfo = jcrtInfo;
		try {
			this.session = this.provider.getSession(null);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException: Get Session Faild ! ",e);
		}
	}
	public JcrtSessionImpl(Provider provider,XmlJcrtInfo jcrtInfo,String workspace) throws CoreException{
		this.provider = provider;
		this.jcrtInfo = jcrtInfo;
		try {
			this.session = this.provider.getSession(workspace);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException: Get Session Faild ! ",e);
		}
	}
	public Query createQuery(Class theClass, String path ,ConditionData conditionData) throws CoreException {
		log.debug("JcrtSessionImpl.createQuery>input object = "+theClass.getName());
		log.debug("JcrtSessionImpl.createQuery>input path = "+path);
		log.debug("JcrtSessionImpl.createQuery>conditionData = "+conditionData.getConditions().toString());
		BaseInfo baseInfo = (BaseInfo)initObject(theClass,path);
		QueryImpl impl = null;
		try {
			String ns = baseInfo.getXmlJcrtInfoDetail().getNamespace();
			StringBuffer sb = new StringBuffer();
			sb.append(baseInfo.getRelPath());
			sb.append("//*");
			List<String> conlist = conditionData.getConditions();
			for(int i=0;i<conlist.size();i++){
				String cons = conlist.get(i);
				//add namespace 
				sb.append(cons);
			}
			String sql = UtilHelper.replace(sb.toString(),"][", " and ");
			QueryManager qm = session.getWorkspace().getQueryManager();
			impl = new QueryImpl(qm,baseInfo,sql);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException",e);
		} catch (Exception e){
			throw new CoreException("Exception",e);
		}
		return impl;
	}

	public Query createQuery(Class theClass, String path) throws CoreException {
		log.debug("JcrtSessionImpl.createQuery>input object = "+theClass.getName());
		log.debug("JcrtSessionImpl.createQuery>input path = "+path);
		BaseInfo baseInfo = (BaseInfo)initObject(theClass,path);
		QueryImpl impl = null;
		try {
			QueryManager qm = session.getWorkspace().getQueryManager();
			impl = new QueryImpl(qm,baseInfo,null);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException",e);
		} catch (Exception e){
			throw new CoreException("Exception",e);
		}
		return impl;
	}
	public void delete(Class theClass, String path) throws CoreException {
		log.debug("JcrtSessionImpl.delete>input object = "+theClass.getName());
		log.debug("JcrtSessionImpl.delete>input path = "+path);
		BaseInfo baseInfo = (BaseInfo)initObject(theClass,path);
		try {
			if(baseInfo.getNodeType().equals(ItemType.NODE)){
				//Node
				NodeData nodeData = new NodeData(baseInfo.getNode());
				nodeData.delete();
			}
			if(baseInfo.getNodeType().equals(ItemType.CONTENT)){
				//Content
				ContentData nodeData = new ContentData(baseInfo.getNode());
				nodeData.delete();
			}
		} catch (PathNotFoundException e) {
			throw new CoreException("PathNotFoundException",e);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException",e);
		} catch (Exception e){
			throw new CoreException("Exception",e);
		}
	}

	public void delete(Object object) throws CoreException {
		log.debug("JcrtSessionImpl.delete>input object = "+object.getClass().getName());
		BaseInfo baseInfo = (BaseInfo)initObject(object);
		try {
			if(baseInfo.getNodeType().equals(ItemType.NODE)){
				//Node
				NodeData nodeData = new NodeData(baseInfo.getNode());
				nodeData.delete();
			}
			if(baseInfo.getNodeType().equals(ItemType.CONTENT)){
				//Content
				ContentData nodeData = new ContentData(baseInfo.getNode());
				nodeData.delete();
			}
		} catch (PathNotFoundException e) {
			throw new CoreException("PathNotFoundException",e);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException",e);
		}  catch (Exception e){
			throw new CoreException("Exception",e);
		}
	}

	public Version getVersion(Class theClass, String path) throws CoreException {
		log.debug("JcrtSessionImpl.getVersion>input object = "+theClass.getName());
		log.debug("JcrtSessionImpl.getVersion>input path = "+path);
		BaseInfo baseInfo = (BaseInfo)this.load(theClass, path);
		if(baseInfo == null || !baseInfo.getClassName().equals(theClass.getName())){
			return null;
		}
		Version v = null;
		if(baseInfo.getNodeType().equals(ItemType.NODE)){
			//Node
			throw new CoreException(theClass.getName() + "is not ItemType.Content ");
		}
		if(baseInfo.getNodeType().equals(ItemType.CONTENT)){
			//Content
			v = new VersionImpl(baseInfo.getNode(),baseInfo);
		}
		return v;
	}

	public Object load(Class theClass, String path) throws CoreException {
		log.debug("JcrtSessionImpl.load>input object = "+theClass.getName());
		log.debug("JcrtSessionImpl.load>input path = "+path);
		BaseInfo baseInfo = (BaseInfo)initObject(theClass,path);
		Object obj = null;
		try {
			if(baseInfo == null){
				return null;
			}
			Node node = baseInfo.getNode();
			if(node == null){
				return null;
			}
			if(baseInfo.getNodeType().equals(ItemType.NODE)){
				//Node
				NodeData nodeData = new NodeData(node);
				obj = nodeData.changeToInfoClass(baseInfo);
			}
			if(baseInfo.getNodeType().equals(ItemType.CONTENT)){
				//Content
				ContentData nodeData = new ContentData(node);
				obj = nodeData.changeToInfoClass(baseInfo);
			}
			if(obj!=null && baseInfo.getClassName().equals(((BaseInfo)obj).getClassName())){
				return obj;
			}else{
				return null;
			}
		} catch (PathNotFoundException e) {
			throw new CoreException("PathNotFoundException",e);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException",e);
		}  catch (Exception e){
			throw new CoreException("Exception",e);
		}
	}

	public Serializable save(Object object, String path) throws CoreException {
		log.debug("JcrtSessionImpl.save>input object = "+object.getClass().getName());
		log.debug("JcrtSessionImpl.save>input path = "+path);
		try {
			DependHelper.dependOn(object, "path", path);
		} catch (Exception e) {
			throw new CoreException("RepositoryException: Set BaseInfo Data Faild ! ",e);
		}
		BaseInfo baseInfo = (BaseInfo)initObject(object);
		log.debug("JcrtSessionImpl.save>baseInfo.getRelPath() = "+baseInfo.getRelPath());
		try {
			if(baseInfo.getNodeType().equals(ItemType.NODE)){
				log.debug("JcrtSessionImpl.save>is Node");
				//Node
				NodeData nodeData = new NodeData(baseInfo.getRootNode(),baseInfo.getRelPath(),true);
				nodeData.setAttribute(object);
				nodeData.save();
			}
			if(baseInfo.getNodeType().equals(ItemType.CONTENT)){
				log.debug("JcrtSessionImpl.save>is Content ");
				//Content
				ContentData nodeData = new ContentData(baseInfo.getRootNode(),baseInfo.getRelPath(),true);
				nodeData.setAttribute(object);
				nodeData.save();
			}
		} catch (PathNotFoundException e) {
			throw new CoreException("PathNotFoundException",e);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException",e);
		}  catch (Exception e){
			throw new CoreException("Exception",e);
		}
		return (Serializable)object;
	}
	
	private Object initObject(Class theClass,String path) throws CoreException{
		Object obj = null;
		try {
			BaseInfo baseInfo = (BaseInfo)theClass.newInstance();;
			baseInfo.setPath(path);
			obj = initObject(baseInfo);
		} catch (InstantiationException e) {
			throw new CoreException("InstantiationException",e);
		} catch (IllegalAccessException e) {
			throw new CoreException("IllegalAccessException",e);
		} catch (Exception e){
			throw new CoreException("Exception",e);
		}
		return obj;
	}
	
	private Object initObject(Object object) throws CoreException{
		BaseInfo baseInfo = (BaseInfo)object;
		if(this.session == null){
			try {
				this.session = this.provider.getSession(jcrtInfo.getXmlJcrtInfoDetail(object.getClass()).getWorkspacename());
			} catch (RepositoryException e) {
				throw new CoreException("RepositoryException: Get Session Faild ! ",e);
			}			
		}
		try {
			Node root = session.getRootNode();
			DependHelper.dependOn(object, "rootNode", root);
		} catch (Exception e) {
			throw new CoreException("RepositoryException: Set BaseInfo Data Faild ! ",e);
		}
		return object;
	}

	public List<Object> getChildren(Class theClass, String path)
			throws CoreException {
		log.debug("JcrtSessionImpl.getChildren>input Class = "+theClass.getName());
		log.debug("JcrtSessionImpl.getChildren>input path = "+path);
		BaseInfo baseInfo = (BaseInfo)initObject(theClass,path);
		List<Object> clist = new ArrayList<Object>();
		try {
			String ns = this.jcrtInfo.getXmlJcrtInfoDetail(theClass).getNamespace();
			NodeIterator it = null;
			log.debug("JcrtSessionImpl.getChildren>RelPath = "+baseInfo.getRelPath());
			
			if(baseInfo.getRelPath() == null || baseInfo.getRelPath().length() == 0){
				it = baseInfo.getRootNode().getNodes();
			}else{
				if(baseInfo.getNode()!=null)
					it = baseInfo.getNode().getNodes();
			}
			if (it == null || it.getSize() < 1){
				return clist;
			}
			while (it.hasNext()) {
				Node n = it.nextNode();
				NodeData nodeData = new NodeData(n);
				if(n.getName().indexOf(ns)!=-1){
					BaseInfo info = (BaseInfo)baseInfo.clone();
					info = (BaseInfo)nodeData.changeToInfoClass(info);
					if(baseInfo.getClassName().equals(info.getClassName())){
						log.debug("JcrtSessionImpl.getChildren>Child RelPath = "+baseInfo.getRelPath()+"/"+n.getName());
						info.setRelPath(baseInfo.getRelPath()+"/"+n.getName());
						info.setNode(n);
						clist.add(info);	
					}
				}
			}
		} catch (CloneNotSupportedException e) {
			throw new CoreException("CloneNotSupportedException",e);
		} catch (PathNotFoundException e) {
			throw new CoreException("PathNotFoundException",e);
		} catch (RepositoryException e) {
			throw new CoreException("RepositoryException",e);
		} catch (Exception e){
			throw new CoreException("Exception",e);
		}
		log.debug("JcrtSessionImpl.getChildren>List size = "+clist.size());
		return clist;
	}

}
